module i_cache(

    //global
    input               clock,
    input               reset,

    //cpu <-> cache
    input               req,             // 1:write 0:read
    input               req_valid,       // try a pluse signal 
    input      [63:0]   rw_addr,

    output              addr_resp,       // read:读地址被接收 write：写地址和写数据被接收
    output              data_resp,       // read: data_valid  write: write_respone
    output wire [63:0]  rdata, 

    //axi <-> cache
    output reg          axi_r_req,
    output wire [63:0]  axi_r_addr,

    input               axi_r_ready,      //axi读请求能否被响应
    input               axi_r_valid,      //axi读有效
    input  wire [255:0] axi_rdata

);
    //index,tag,offset
    wire [4:0]  index;
    wire [20:0] tag;
    wire [4:0]  offset;
    assign {tag,index,offset} = rw_addr[30:0];
    //req buffer
    reg         req_reg;
    reg         req_valid_reg;
    reg  [4:0]  index_reg;
    reg  [20:0] tag_reg;
    reg  [4:0]  offset_reg;
    reg  [63:0] rw_addr_reg;


    //RLU 
    reg  [0:0] rlu_reg[0:31];              // 0表示way0最近没使用过，1表示way1最近没用过
    //cache tag
    wire [20:0] way0_r_tag;
    wire [20:0] way1_r_tag;
    // cache valid
    reg  [0:0]  way0_valid[0:31];
    reg  [0:0]  way1_valid[0:31];
    //cache data page
    wire [255:0] way0_wdata;
    wire [255:0] way1_wdata;
    wire [255:0] way0_rdata;
    wire [255:0] way1_rdata;
    //cache hit
    wire         way0_hit;
    wire         way1_hit;
    wire         cache_hit;
    assign way0_hit  = way0_valid[index_reg] &( way0_r_tag == tag_reg);
    assign way1_hit  = way1_valid[index_reg] &( way1_r_tag == tag_reg);
    assign cache_hit = way0_hit | way1_hit;

    //rd/wr ctr signal
    wire         req_hs;
    wire [63:0]  rdata_hit;  
    wire [63:0]  rdata_hit0;  //way0
    wire [63:0]  rdata_hit1;  //way1
    wire [63:0]  rdata_miss;
    assign req_hs     = req_valid & addr_resp;
    assign addr_resp  = (main_state == IDLE)|((main_state == LOOKUP) & cache_hit & req);
    assign data_resp  = ((main_state == LOOKUP) & cache_hit) | axi_r_valid;
    assign rdata_hit0 = (offset_reg[4:3] == 2'b11) ? way0_rdata[255:192] :
                        (offset_reg[4:3] == 2'b10) ? way0_rdata[191:128] :
                        (offset_reg[4:3] == 2'b01) ? way0_rdata[127: 64] :
                        (offset_reg[4:3] == 2'b00) ? way0_rdata[63:  0] : 0;

    assign rdata_hit1 = (offset_reg[4:3] == 2'b11) ? way1_rdata[255:192] :
                        (offset_reg[4:3] == 2'b10) ? way1_rdata[191:128] :
                        (offset_reg[4:3] == 2'b01) ? way1_rdata[127: 64] :
                        (offset_reg[4:3] == 2'b00) ? way1_rdata[63:  0] : 0;

    assign rdata_hit  = way1_hit ? rdata_hit1 :
                        way0_hit ? rdata_hit0 : 0;

    assign rdata_miss = (offset_reg[4:3] == 2'b11) ? axi_rdata[255:192] :
                        (offset_reg[4:3] == 2'b10) ? axi_rdata[191:128] :
                        (offset_reg[4:3] == 2'b01) ? axi_rdata[127: 64] :
                        (offset_reg[4:3] == 2'b00) ? axi_rdata[63:  0] : 0;
    
    assign rdata      = ((main_state == LOOKUP) & cache_hit) ? rdata_hit :
                        axi_r_valid ? rdata_miss : 0;
    assign axi_r_req  = main_state == REPLACE; 
    assign axi_r_addr = {rw_addr_reg[63:5],5'b0};

    wire  way0_r_ena;
    wire  way1_r_ena;
    wire  way0_w_ena;
    wire  way1_w_ena;
    assign way0_r_ena = req_hs;
    assign way1_r_ena = req_hs;
    assign way0_w_ena = axi_r_valid & (~way0_valid[index_reg] |(way0_valid[index_reg] & way1_valid[index_reg] & ~rlu_reg[index_reg]));
    assign way1_w_ena = axi_r_valid & way0_valid[index_reg] & (~way1_valid[index_reg] |(way1_valid[index_reg] & rlu_reg[index_reg]));

    
    //main state machine

    localparam IDLE    = 0;
    localparam LOOKUP  = 1;
    localparam REPLACE = 2;
    localparam REFILL  = 3;


    reg   [1:0]    main_state;
    reg   [0:0]    sub_state;
    

    //state machine

    always@(posedge clock)begin
        if(reset)begin
           mian_state <= 0;
        end
        else begin
           case(main_state)
            IDLE    :
                begin 
                    if(req_valid) 
                        main_state <= LOOKUP;
                    else
                        main_state <= IDLE; 
                end  
            LOOKUP  :
                begin
                    if(cache_hit) 
                        main_state <= IDLE;
                    else
                        main_state <= REPLACE; 
                end 
            REPLACE :
                begin 
                    if(axi_r_ready) 
                        main_state <= REFILL;
                    else
                        main_state <= main_state;
                end
            REFILL  :
            begin 
                    if(axi_r_valid) 
                        main_state <= IDLE;
                    else
                        main_state <= main_state;
            default :   main_state <= main_state;
           endcase
        end
    end

    


    // req buffer 只有在能够接收请求的时候才缓存

    always@(posedge clock)begin
        if(reset)begin
            req_reg       <= 0 ;
            req_valid_reg <= 0 ;
            index_reg     <= 0 ;
            tag_reg       <= 0 ;
            offset_reg    <= 0 ;
            rw_addr_reg   <= 0 ;
        end
        else if(req_hs)begin
            req_reg       <= req        ;
            req_valid_reg <= req_valid  ;
            index_reg     <= index      ;
            tag_reg       <= tag        ;
            offset_reg    <= offset     ;
            rw_addr_reg   <= rw_addr_reg;
        end
        else begin
            req_reg       <= req_reg       ;
            req_valid_reg <= req_valid_reg ;
            index_reg     <= index_reg     ;
            tag_reg       <= tag_reg       ;
            offset_reg    <= offset_reg    ;
            rw_addr_reg   <= rw_addr_reg   ;
        end
    end


    // rlu page  0: 最近way0未被使用过 1： 最近1未被使用过
    always@(posedge clock)begin
        if(reset)begin
            rlu_reg[0:31] <= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
        end
        else if((way0_hit & main_state == LOOKUP) | way0_w_ena))
            rlu_reg[index_reg] <= 1'b1;
        else if((way1_hit &(main_state == LOOKUP) | way1_w_ena))
            rlu_reg[index_reg] <= 1'b0;
    end
    // cache valid
    always@(posedge clock)begin
        if(reset)begin
            way0_valid[0:31] <= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
            way1_valid[0:31] <= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
        end
        else if(way0_w_ena)
            way0_valid[index_reg] <= 1'b1;
        else if(way1_w_ena)
            way1_valid[index_reg] <= 1'b1;
    end
    // cache page
    // cen 使能信号，低电平有效
    // wen 写使能信号，低电平有效

    S011HD1P_X32Y2D128 #(
        .Bits       		( 22 		),
        .Word_Depth 		( 32  		),
        .Add_Width  		( 5   		))
    way0_tag_page(
        //ports
        .Q   		( way0_r_tag   		        ),
        .CLK 		( clock 		            ),
        .CEN 		( ~(way0_r_ena | way0_w_ena ),
        .WEN 		(  way0_r_ena 		        ),
        .A   		( index   		            ),
        .D   		( tag_reg   		        )
    );

    S011HD1P_X32Y2D128 #(
        .Bits       		( 22 		        ),
        .Word_Depth 		( 32  		        ),
        .Add_Width  		( 5   		        ))
    way1_tag_page(
        //ports
        .Q   		( way1_r_tag   		        ),
        .CLK 		( clock 		            ),
        .CEN 		( ~(way1_r_ena | way1_w_ena ),
        .WEN 		(  way1_r_ena 		        ),
        .A   		( index   		            ),
        .D   		( tag_reg                   )
    );

    S011HD1P_X32Y2D128 #(
        .Bits       		( 256		        ),
        .Word_Depth 		( 32  		        ),
        .Add_Width  		( 5   		        ))
    way0_data_page(
        //ports
        .Q   		( way0_rdata           		),
        .CLK 		( clock 		            ),
        .CEN 		( ~(way0_r_ena | way0_w_ena)),
        .WEN 		(  way0_r_ena 		        ),
        .A   		( index   		            ),
        .D   		( way0_wdata                )
    );

    S011HD1P_X32Y2D128 #(
        .Bits       		( 256		        ),
        .Word_Depth 		( 32  		        ),
        .Add_Width  		( 5   		        ))
    way1_data_page(
        //ports
        .Q   		( way1_rdata           		),
        .CLK 		( clock 		            ),
        .CEN 		( ~(way0_r_ena | way0_w_ena)),
        .WEN 		(  way0_r_ena 		        ),
        .A   		( index   		            ),
        .D   		( way1_wdata                )
    );

endmodule